"use strict";
var addNode = function(el, tagName, place, obj)
{
    var node = document.createElement(tagName);
    for(var i in obj)
    {
        if(i == "style")
        {
            for(var j in obj["style"])
            {
                node.style[j] = obj["style"][j];
            }
        }
        else
        {
            node.setAttribute(i, obj[i]);
        }
    }

    switch(place)
    {
        case "innerFirst": el.insertBefore(node, el.firstChild); break;
        case "before": el.parentNode.insertBefore(node, el); break;
        case "innerEnd": el.appendChild(node); break;
        default: break;
    }
    return node;
}

var random = function(min, max)
{
    return parseInt(Math.random() * (max - min) + min);
}

var Lock = function()
{
    var lock = {};
    var queue = [];
    this.get = function()
    {
        var sum = 0;
        for(var i in lock)
        {
            sum += lock[i];
        }
        return sum;
    }

    this.add = function(id)
    {
        if(lock[id] === undefined)
        {
            lock[id] = 1;
        }
        else
        {
            lock[id] ++;
        }
    }

    this.sub = function(id)
    {
        if(lock[id] == 1)
        {
            lock[id] --;
            while(this.get() == 0 && queue.length != 0)
            {
                queue.shift()();
            }
        }
    }

    this.callback = function(func)
    {
        queue.push(func);
    }
}

// 记录块布局
var layout = {};

// 记录有多少个数块
var lenth = 0;

// 操作锁，防止操作过快
var lock = new Lock();

var block = function(parentNode, x, y)
{
    length ++;

    var queue = [];
    this.x = x;
    this.y = y;
    this.coordinate = x + "-" + y;
    this.newBlock = true;
    this.el = addNode(parentNode, "div", "innerEnd", {
        style: {
            left: this.x * 142 + 10 + "px",
            top: this.y * 142 + 10 + "px"
        }
    });
    this.setNumber(Math.random() < 0.1 ? 4 : 2);
    this.setTransitionEndCallBack = function(func)
    {
        queue.push(func);
    }
    setTimeout(function(){
        this.birth();
    }.bind(this));
    this.el.addEventListener("transitionend", function(){
        lock.sub(this.coordinate);
        this.coordinate = this.x + "-" + this.y;
        while(queue.length != 0)
        {
            queue.shift()();
        }
    }.bind(this));
}

block.prototype.birth = function()
{
    this.el.style.opacity = 1;
    this.el.style.transform = "scale(1)";
}

block.prototype.setNumber = function(number)
{
    this.number = number;
    this.el.setAttribute("class", "block" + number);
    this.el.innerHTML = this.number;
}

block.prototype.destroy = function()
{
    this.el.parentNode.removeChild(this.el);
    delete layout[this.x + "" + this.y];
    length --;
}

block.prototype.move = function()
{
    var style = this.el.style;
    if(parseInt(style.top) != this.y * 142 + 10 
        || parseInt(style.left) != this.x * 142 + 10)
    {
        style.top = this.y * 142 + 10 + "px";
        style.left = this.x * 142 + 10 + "px";
        lock.add(this.coordinate);
    }
    this.newBlock = false;
}

block.prototype.setCoordinate = function(x, y)
{
    if(x < 0 || x > 3 || y < 0 || y >3)
    {
        return false;
    }
    var target = layout[x + "-" + y];
    if(target !== undefined)
    {
        if(target.newBlock)
        {
            return false;
        }
        var bl = layout[this.x + "-" + this.y];
        if(bl.number == target.number)
        {
            delete layout[this.x + "-" + this.y];
            this.x = x;
            this.y = y;
            bl.move();
            bl.setTransitionEndCallBack(function(){
                bl.destroy();
            });
            var newBlock = new block(box, x, y);
            newBlock.setNumber(this.number * 2);
            newBlock.newBlock = true;
            newBlock.el.style.zIndex = parseInt(target.el.style.zIndex) + 1;
            newBlock.setTransitionEndCallBack(function(){
                target.destroy();
            });
            layout[x + "-" + y] = newBlock;
        }
        return false;
    }
    if(this.x != x || this.y != y)
    {
        layout[x + "-" + y] = layout[this.x + "-" + this.y];
        delete layout[this.x + "-" + this.y];
        this.x = x;
        this.y = y;
        return true;
    }
    else
    {
        return false;
    }
}

function gameOver()
{
    alert("游戏结束！");
}

var box = document.getElementById("move");
function randomBlock()
{
    if(length < 16)
    {
        var x, y;
        do
        {
            x = random(0, 4);
            y = random(0, 4);
        }while(layout[x + "-" + y] !== undefined);
        layout[x + "-" + y] = new block(box, x, y);
        return true;
    }
    return false;
}

function move(offsetX, offsetY)
{
    var zIndex = 0;
    var x = [0, 1, 2, 3];
    var y = [0, 1, 2, 3];
    if(offsetX == 1)
    {
        x.reverse();
    }
    if(offsetY == 1)
    {
        y.reverse();
    }

    for(var i = 0; i < x.length; i ++)
    {
        for(var j = 0; j < y.length; j ++)
        {
            var block = layout[x[i] + "-" + y[j]];
            if(block !== undefined)
            {
                block.el.style.zIndex = 16 - zIndex ++;
                while(block.setCoordinate(block.x + offsetX, block.y + offsetY));
                block.move();
            }
        }
    }
    if(lock.get() != 0)
    {
        lock.callback(randomBlock);
    }
}

document.onkeydown = function(event)
{
    if(lock.get() == 0)
    {
        console.table(layout);
        console.log(event.key);
        switch(event.key)
        {
            case "ArrowUp": move(0, -1); break;
            case "ArrowDown":  move(0, 1); break;
            case "ArrowLeft":  move(-1, 0); break;
            case "ArrowRight":  move(1, 0); break;
        }
    }
}

function randomBlockTest(x, y, n)
{
    layout[x + "-" + y] = blockTest(x, y, n);
    return true;
}

function blockTest(x, y, n)
{
    var temp = new block(box, x, y);
    temp.setNumber(n);
    return temp;
}

randomBlock();
randomBlock();